home *** CD-ROM | disk | FTP | other *** search
/ Amiga Collections: Topik / Topik - Disk 02 - Fonts and CLI Commands (19xx)(Topik Public Domain)(PD)[a][WB].zip / Topik - Disk 02 - Fonts and CLI Commands (19xx)(Topik Public Domain)(PD)[a][WB].adf / Source / diffdir.c < prev    next >
C/C++ Source or Header  |  1989-04-19  |  21KB  |  758 lines

  1. /*  DiffDir - Compare directories for differences.
  2.     Filename:   DiffDir.c
  3.     (C)Copyright 1988 by Mark R. Rinfret, All Rights Reserved.
  4.     This software may be freely distributed for non-profit use only.
  5.     You are free to make changes and redistribute this program as
  6.     long as the source is distributed and this notice is kept intact.
  7.  
  8.     History (most recent change first):
  9.  
  10.     12/31/88 V1.0 (MRR)
  11.         Program conception and implementation.
  12.  
  13.     I wrote DiffDir to assist me with configuration management.  Though
  14.     I keep all of my PD files on floppy disk, I usually roll them onto
  15.     the hard disk when I want to make changes.  Sometimes, I forget to
  16.     copy the hard disk version back to floppy or I forget that I've already
  17.     done it.  DiffDir scans two directories and reports the following
  18.     discrepancies:
  19.  
  20.         1. File dates are different.
  21.         2. File protection flags are different.
  22.         3. File names are not exact (case discrepancy).
  23.         4. File sizes are different.
  24.         5. File comments are different.
  25.         6. File exists in one directory but not the other.
  26.  
  27.     DiffDir does not perform file content comparisons.  It will, however,
  28.     optionally generate a script for performing comparisons on files whose
  29.     attributes differ.
  30.  
  31.     Usage:  DiffDir [-c] [-s scriptfile] [-v] <dir1> <dir2>
  32.     Where:
  33.             -c specifies that letter case should be ignored when comparing
  34.                filenames
  35.  
  36.             -s specifies that a file comparison script is to be output
  37.  
  38.             -v specifies verbose output
  39.  
  40.             <dir1> is the name of the first directory
  41.  
  42.             <dir2> is the name of the second directory
  43.  
  44. */
  45.  
  46. #include <stdio.h>
  47. #include <exec/types.h>
  48. #include <exec/memory.h>
  49. #include <libraries/dos.h>
  50. #include <functions.h>
  51.  
  52.  
  53. typedef struct fileList {
  54.     USHORT          fileCount;
  55.     char            *dName;     /* directory name for this list */
  56.     struct fileNode *firstEntry, *lastEntry;
  57.     } FileList;
  58.  
  59. typedef struct fileNode {
  60.     struct fileNode *next, *prev;
  61.     struct fileList *parentList;    /* the list that I belong to */
  62.     char            *name;
  63.     LONG            flags;          /* protection, other bits */
  64.     char            *comment;       /* NULL if comment was empty */
  65.     struct DateStamp date;
  66.     ULONG           size;           /* in bytes */
  67.     BOOL            isDir;          /* TRUE => node is a directory */
  68.     struct FileNode *subList;       /* sublist for directory node */
  69.     } FileNode;
  70.  
  71.  
  72. char                    *DupString();
  73. FileNode                *FindFile();
  74. void                    FreeNode();
  75. char                    *MakeDirName();
  76. void                    *MyAlloc();
  77. void                    MyExit();
  78. void                    ReportStats();
  79. void                    WriteFileInfo();
  80.  
  81.  
  82. struct FileInfoBlock    *fib;
  83. BOOL                    ignoreCase = FALSE;
  84. USHORT                  level = 0;
  85. FileList                list1, list2;
  86. LONG                    maxMemUsed, memInUse;
  87. BOOL                    outputScript = FALSE;
  88. FILE                    *scriptFile;
  89. LONG                    totalFiles, totalDirs;
  90. BOOL                    verbose = FALSE;
  91.  
  92. main(argc, argv)
  93.     int argc; char **argv;
  94. {
  95.     char    flag;
  96.  
  97.     while (--argc > 0 && **++argv == '-') {
  98.         flag = (*argv)[1];
  99.         switch (flag) {
  100.             case 'c':
  101.                 ignoreCase = TRUE;
  102.                 break;
  103.             case 's':
  104.                 if (--argc) {
  105.                     ++argv;
  106.                     scriptFile = fopen(*argv, "w");
  107.                     if (!scriptFile) {
  108.                         perror("Script file would not open!");
  109.                         exit(1);
  110.                     }
  111.                 }
  112.                 else
  113.                     Usage();
  114.                 break;
  115.             case 'v':
  116.                 verbose = TRUE;
  117.                 break;
  118.             default:
  119.                 Usage();
  120.         }
  121.     }
  122.     if (argc != 2) Usage();
  123.     list1.dName = MakeDirName("",*argv++);
  124.     list2.dName = MakeDirName("",*argv);
  125.     /* fib must be longword aligned, thus the AllocMem call. */
  126.     fib = AllocMem((long) sizeof(*fib), MEMF_PUBLIC|MEMF_CLEAR);
  127.     if (fib == NULL) {
  128.         printf("DiffDir: unable to allocate file info block!\n");
  129.         goto done;
  130.     }
  131.  
  132.     if (! CollectFiles(&list1))
  133.         if (! CollectFiles(&list2))
  134.             CompareLists(&list1, &list2);
  135. done:
  136.     if (fib) FreeMem(fib, (long) sizeof(*fib));
  137.     if (verbose) ReportStats();
  138. }
  139.  
  140. /*  FUNCTION
  141.         AddNode - add file info node to list.
  142.  
  143.     SYNOPSIS
  144.         AddNode(node, list)
  145.             FileNode *node;
  146.             FileList *list;
  147.  
  148.     DESCRIPTION
  149.         AddNode adds the <node> to the <list>.  Right now, a very lazy
  150.         approach is taken (adds to end of list).  Perhaps later, we'll
  151.         make the list a binary tree or better.
  152.  
  153. */
  154.  
  155. void
  156. AddNode(node, list)
  157.     FileNode *node; FileList *list;
  158. {
  159.     if (list->firstEntry) {         /* List has stuff in it? */
  160.         list->lastEntry->next = node;
  161.     }
  162.     else {
  163.         list->firstEntry = node;    /* This is the first entry. */
  164.     }
  165.     node->prev = list->lastEntry;
  166.     list->lastEntry = node;
  167.     ++list->fileCount;
  168.     if (node->isDir)
  169.         ++totalDirs;
  170.     else
  171.         ++totalFiles;
  172. }
  173.  
  174. /*  FUNCTION
  175.         CollectFiles - collect files for one directory level.
  176.  
  177.     SYNOPSIS
  178.         int CollectFiles(list)
  179.             FileList *list;
  180.  
  181.     DESCRIPTION
  182.         CollectFiles scans the directory pointed to by <list> and creates
  183.         list entry nodes for each file or directory found.  A zero is
  184.         returned on success, non-zero otherwise.
  185. */
  186.  
  187. int
  188. CollectFiles(list)
  189.     FileList *list;
  190. {
  191.     int         errCode;
  192.     struct Lock *lock = NULL;
  193.     FileNode    *fNode;
  194.     int         result = 0;
  195.  
  196.     if (verbose)
  197.         printf("DiffDir: scanning '%s'\n", list->dName);
  198.  
  199.     lock = (struct Lock *) Lock(list->dName, SHARED_LOCK);
  200.     if (lock == NULL) {
  201.         result = IoErr();
  202.         printf("DiffDir: failed to lock '%s'!\n", list->dName);
  203.         goto done;
  204.     }
  205.     if (Examine(lock, fib) == 0) {
  206.         result = IoErr();
  207.         printf("DiffDir: failed to get info for '%s'!\n", list->dName);
  208.         goto done;
  209.     }
  210.  
  211.     if (fib->fib_DirEntryType < 0) {
  212.         result = -1;
  213.         printf("DiffDir: '%s' is not a directory!\n", list->dName);
  214.         goto done;
  215.     }
  216.  
  217.     while (!result && ExNext(lock, fib)) {
  218.         fNode = MyAlloc(sizeof(FileNode));
  219.         fNode->parentList = list;
  220.         fNode->name = DupString(fib->fib_FileName);
  221.         fNode->isDir = (fib->fib_DirEntryType > 0);
  222.         fNode->flags = fib->fib_Protection;
  223.         if (*fib->fib_Comment)
  224.             fNode->comment = DupString(fib->fib_Comment);
  225.         fNode->date = fib->fib_Date;
  226.         fNode->size = fib->fib_Size;
  227.         AddNode(fNode, list);
  228.     }
  229.     errCode = IoErr();
  230.     if (errCode != ERROR_NO_MORE_ENTRIES) {
  231.         result = errCode;
  232.         printf("DiffDir: scan of directory '%s' failed!\n", list->dName);
  233.     }
  234. done:
  235.     if (lock) UnLock(lock);
  236.     return result;
  237. }
  238.  
  239. /*  FUNCTION
  240.         CompareLists - compare files and directories in two lists.
  241.  
  242.     SYNOPSIS
  243.         int CompareLists(l1, l2)
  244.             FileList *l1, *l2;
  245.  
  246.     DESCRIPTION
  247.         Comparelists first makes an overall assessment of lists <l1> and
  248.         <l2>.  If the number of files/directories in the lists differ,
  249.         that fact is reported.  Next, CompareLists tests for the condition
  250.         where an entry in one list has the same name as an entry in the
  251.         other list, but one entry represents a file and the other entry
  252.         represents a directory.  These entries are removed from the list.
  253.         CompareFiles is then called to compare all file nodes, removing
  254.         them as they are "used".  Finally, CompareDirs is called to
  255.         compare all directory nodes, again removing the nodes as they
  256.         are used. A non-zero return indicates a fatal error.
  257. */
  258. int
  259. CompareLists(l1, l2)
  260.     FileList *l1, *l2;
  261. {
  262. static char *isDirMsg =     " is a directory";
  263. static char *isFileMsg =    " is a file";
  264.  
  265.     FileNode    *f1, *f2, *nextEntry;
  266.     int         i;
  267.     int result = 0;
  268.  
  269.     ++level;
  270.     if (verbose) {
  271.         printf("DiffDir: comparing directory\n '%s' to '%s'\n",
  272.                l1->dName, l2->dName);
  273.     }
  274.     /* Scan the lists for nodes whose names match but whose types
  275.        differ (file vs. directory).
  276.     */
  277.     for (f1 = l1->firstEntry; f1; f1 = nextEntry) {
  278.         nextEntry = f1->next;
  279.         f2 = FindFile(f1, l2);
  280.         if (f2 && (f2->isDir != f1->isDir) ) {  /* Ooops! */
  281.             printf("*** '%s%s' %s\n*** but '%s%s' %s!\n",
  282.                    l1->dName,f1->name, f1->isDir ? isDirMsg : isFileMsg,
  283.                    l2->dName,f2->name, f2->isDir ? isDirMsg : isFileMsg);
  284.             FreeNode(f1, l1);
  285.             FreeNode(f2, l2);
  286.         }
  287.     }
  288.     if (! (result = CompareFiles(l1, l2)))
  289.         result = CompareDirs(l1, l2);
  290.     --level;
  291.     return result;
  292. }
  293.  
  294. /*  FUNCTION
  295.         CompareDirs - compare directory entries.
  296.  
  297.     SYNOPSIS
  298.         int CompareDirs(list1, list2)
  299.             FileList *list1, *list2;
  300.  
  301.     DESCRIPTION
  302.         CompareDirs scans <list1>, attempting to match its directory node
  303.         entries with entries in <list2>.  For each matching entry found,
  304.         CompareDirs creates a new sublist, recursively calling CompareLists
  305.         to compare the contents of those directories.  When CompareLists
  306.         returns, CompareDirs removes the "used" directory entries from
  307.         both lists. A non-zero return code indicates a fatal error.
  308. */
  309.  
  310. int
  311. CompareDirs(list1, list2)
  312.     FileList *list1, *list2;
  313. {
  314. static char *missing = "*** Directory missing: '%s%s'\n";
  315.  
  316.     FileNode *n1, *n2, *nextEntry;
  317.     int      result = 0;
  318.     FileList *subList1, *subList2;
  319.  
  320.     for (n1 = list1->firstEntry; n1 && !result; n1 = nextEntry) {
  321.         nextEntry = n1->next;
  322.         /* Note: there should only be directory nodes in the list
  323.            at this point!
  324.         */
  325.         if (! n1->isDir) {
  326.             puts("DiffDir: non-directory node found in CompareDirs!");
  327.             MyExit();                   /* Dis be real bad! */
  328.         }
  329.         n2 = FindFile(n1, list2);
  330.         if (n2 == NULL) {
  331.             printf(missing, list2->dName, n1->name);
  332.         }
  333.         else {
  334.             subList1 = MyAlloc( sizeof(FileList) );
  335.             subList1->dName = MakeDirName(list1->dName, n1->name);
  336.             subList2 = MyAlloc( sizeof(FileList) );
  337.             subList2->dName = MakeDirName(list2->dName, n2->name);
  338.             result = CollectFiles(subList1);
  339.             if (!result)
  340.                 result = CollectFiles(subList2);
  341.             if (!result)
  342.                 result = CompareLists(subList1, subList2);
  343.  
  344.             /* Give back the memories :-) */
  345.             free(subList1->dName);
  346.             free(subList1);
  347.             free(subList2->dName);
  348.             free(subList2);
  349.         }
  350.         FreeNode(n1, list1);
  351.         if (n2) FreeNode(n2, list2);
  352.     }
  353.     if (!result) {
  354.         for (n2 = list2->firstEntry; n2; n2 = nextEntry) {
  355.             nextEntry = n2->next;
  356.             printf(missing, list1->dName, n2->name);
  357.             FreeNode(n2, list2);
  358.         }
  359.     }
  360.     return result;
  361. }
  362.  
  363. /*  FUNCTION
  364.         CompareFile - compare the attributes of two similar files.
  365.  
  366.     SYNOPSIS
  367.         void CompareFile(f1, f2)
  368.              FileNode *f1, *f2;
  369.  
  370.     DESCRIPTION
  371.         CompareFile is called with two file description nodes, <f1> and
  372.         <f2> which are expected to represent similar files in different
  373.         directory hierarchies.  CompareFile will report any discrepancies
  374.         to standard output.
  375. */
  376. void
  377. CompareFile(f1, f2)
  378.     FileNode *f1, *f2;
  379. {
  380.  
  381. #define NAMES_DONT_MATCH    1
  382. #define DATES_DONT_MATCH    2
  383. #define FLAGS_DONT_MATCH    4
  384. #define SIZES_DONT_MATCH    8
  385. #define COMMENTS_DONT_MATCH 16
  386.  
  387. #define ITEM_COUNT          5   /* Make sure this tracks the list above! */
  388.  
  389. static char *errorDesc[ITEM_COUNT] = {
  390.         " names ", " dates ", " flags ", " sizes ", " comments " };
  391.  
  392.     USHORT error = 0, item, mask;
  393.  
  394.     if (f1->isDir != f2->isDir) {
  395.         puts("*** File type mismatch (file vs. dir) - program error!");
  396.         FreeNode(f1, f1->parentList);
  397.         FreeNode(f2, f2->parentList);
  398.     }
  399.     else {
  400.         if (f1->flags != f2->flags)
  401.             error |= FLAGS_DONT_MATCH;
  402.  
  403.         if (CompareDS(&f1->date, &f2->date))
  404.             error |= DATES_DONT_MATCH;
  405.  
  406.         if (!ignoreCase) {
  407.             if (strcmp(f1->name, f2->name) != 0)
  408.                 error |= NAMES_DONT_MATCH;
  409.         }
  410.  
  411.         if (f1->size != f2->size) {
  412.             error |= SIZES_DONT_MATCH;
  413.             if (scriptFile)
  414.                 fprintf(scriptFile,"%%COMPARE%% %s%s %s%s\n",
  415.                         f1->parentList->dName,f1->name,
  416.                         f2->parentList->dName,f2->name);
  417.         }
  418.         if (strcmp(f1->comment, f2->comment) != 0)
  419.             error |= COMMENTS_DONT_MATCH;
  420.     }
  421.     if (error) {                    /* Aw, darn... */
  422.         printf("*** Mismatch: ");
  423.         for (item = 0, mask = 1; item < ITEM_COUNT;
  424.              ++item, mask= (mask << 1)) {
  425.             if (error & mask)
  426.                 printf(errorDesc[item]);
  427.         }
  428.         puts("");
  429.         puts(f1->parentList->dName);
  430.         WriteFileInfo(f1);
  431.         puts("------------------------------------");
  432.         puts(f2->parentList->dName);
  433.         WriteFileInfo(f2);
  434.         puts("====================================");
  435.     }
  436. }
  437.  
  438. /*  FUNCTION
  439.         CompareFiles - compare all file nodes in two lists.
  440.  
  441.     SYNOPSIS
  442.         int CompareFiles(l1, l2)
  443.             FileList *l1, *l2;
  444.  
  445.     DESCRIPTION
  446.         The file attributes for all files in list <l1> are compared to
  447.         those in list <l2>.  Discrepancies are reported to standard
  448.         output.  After all the files in <l1> have been tested, a second
  449.         scan is made over list <l2> for any remaining file nodes.  These
  450.         represent files which were not found in <l1>.  Upon return, all
  451.         file nodes will have been removed from lists <l1> and <l2>,
  452.         leaving behind any directory nodes for CompareDirs().
  453. */
  454.  
  455. int
  456. CompareFiles(l1, l2)
  457.     FileList *l1, *l2;
  458. {
  459.     static char *missing = "*** File missing: '%s%s'\n";
  460.     FileNode    *f1, *f2;
  461.  
  462.     /* Loop through all file entries in list1. */
  463.     for (f1 = l1->firstEntry; f1; f1 = f1->next) {
  464.         if (f1->isDir) continue;
  465.         f2 = FindFile(f1, l2);
  466.         if (f2 == NULL) {
  467.             printf(missing, l2->dName, f1->name);
  468.         }
  469.         else {
  470.             CompareFile(f1, f2);
  471.         }
  472.         FreeNode(f1, l1);
  473.         if (f2)
  474.             FreeNode(f2, l2);
  475.     }
  476.  
  477.     /* Look for "leftovers" in list 2. */
  478.     for (f2 = l2->firstEntry; f2; f2 = f2->next) {
  479.         if (f2->isDir) continue;
  480.         printf(missing, l1->dName, f2->name);
  481.         FreeNode(f2, l2);
  482.     }
  483.     return 0;
  484. }
  485.  
  486. /*  FUNCTION
  487.         DupString - duplicate a string.
  488.  
  489.     SYNOPSIS
  490.         char *DupString(oldString)
  491.               char *oldString;
  492.  
  493.     DESCRIPTION
  494.         DupString dynamically allocates space for a new copy of <oldString>,
  495.         copies <oldString> to the new area and returns a pointer to the new
  496.         string.
  497.  
  498. */
  499.  
  500. char *
  501. DupString(oldString)
  502.     char *oldString;
  503. {
  504.     char *newString;
  505.  
  506.     newString = MyAlloc(strlen(oldString)+1);
  507.     strcpy(newString, oldString);
  508.     return newString;
  509. }
  510.  
  511. /*  FUNCTION
  512.         FindFile - find a file node by name.
  513.  
  514.     SYNOPSIS
  515.         FileNode *FindFile(node, list)
  516.                   FileNode *node;
  517.                   FileList *list;
  518.  
  519.     DESCRIPTION
  520.         FindFile searches <list> for a file description node whose name
  521.         matches the name in <node>.  A case-insensitive name comparison
  522.         is performed.  If the matching entry is found, a pointer to it
  523.         is returned.  Otherwise, NULL is returned.
  524. */
  525.  
  526. FileNode *
  527. FindFile(node, list)
  528.     FileNode *node; FileList *list;
  529. {
  530.     FileNode *tNode;
  531.  
  532.     for (tNode = list->firstEntry; tNode; tNode = tNode->next) {
  533.         if (stricmp(node->name, tNode->name) == 0)
  534.             return tNode;
  535.     }
  536.     return NULL;                    /* Sorry...not found. */
  537. }
  538.  
  539. /*  FUNCTION
  540.         FreeNode - free a file node from a list.
  541.  
  542.     SYNOPSIS
  543.         void FreeNode(node, list)
  544.              FileNode *node;
  545.              FileList *list;
  546. */
  547. void
  548. FreeNode(node, list)
  549.         FileNode *node; FileList *list;
  550. {
  551.     if (node->prev)
  552.         node->prev->next = node->next;
  553.     if (node->next)
  554.         node->next->prev = node->prev;
  555.     if (node == list->firstEntry)
  556.         list->firstEntry = node->next;
  557.     if (node == list->lastEntry)
  558.         list->lastEntry = node->prev;
  559.  
  560.     free(node->name);
  561.     free(node->comment);
  562.     free(node);
  563. }
  564.  
  565. /*  FUNCTION
  566.         MakeDirName - assemble a directory name from components.
  567.  
  568.     SYNOPSIS
  569.         char *MakeDirName(s1, s2)
  570.               char *s1, *s2;
  571.  
  572.     DESCRIPTION
  573.         MakeDirName dynamically allocates a string large enough to hold
  574.         a composite name formed from strings <s1> and <s2>. It also adds
  575.         a directory separator (/) to the end of the new name if the
  576.         new result does not end in a colon (:).  The new name is returned
  577.         as the function result.
  578. */
  579. char *
  580. MakeDirName(s1, s2)
  581.     char *s1, *s2;
  582. {
  583.     char    *index();
  584.  
  585.     char    *dirName;
  586.  
  587.     dirName = MyAlloc(strlen(s1)+strlen(s2)+2);
  588.     strcpy(dirName, s1);
  589.     strcat(dirName, s2);
  590.     if (dirName[strlen(dirName)-1] != ':') strcat(dirName, "/");
  591.     return dirName;
  592. }
  593.  
  594. /*  FUNCTION
  595.         MyAlloc - perform memory allocation with error checking.
  596.  
  597.     SYNOPSIS
  598.         void *MyAlloc(size)
  599.               USHORT size;
  600.  
  601.     DESCRIPTION
  602.         MyAlloc attempts to allocate <size> bytes of memory.  If it fails,
  603.         an error message is sent to standard output and the program is
  604.         terminated.  Otherwise, MyAlloc returns a pointer to the newly
  605.         allocated (zero-filled) memory block.
  606. */
  607. void *
  608. MyAlloc(size)
  609.     USHORT size;
  610. {
  611.     void *calloc();
  612.  
  613.     void *ptr;
  614.  
  615.     ptr = calloc(size, 1);
  616.     if (ptr == NULL) {
  617.         printf("DiffDir: failed to allocate %ld bytes!\n", size);
  618.         MyExit();
  619.     }
  620.     memInUse += size;
  621.     if (memInUse > maxMemUsed) maxMemUsed = memInUse;
  622.     return ptr;
  623. }
  624.  
  625. /*  FUNCTION
  626.         MyExit - terminate program with cleanup.
  627.  
  628.     SYNOPSIS
  629.         void MyExit();
  630.  
  631.     DESCRIPTION
  632.         MyExit simply provides a graceful way for the program to exit,
  633.         performing any necessary cleanup chores.
  634. */
  635. void
  636. MyExit()
  637. {
  638.     if (fib) FreeMem(fib, (long) sizeof(*fib));
  639.     puts("DiffDir: abnormal exit!");
  640.     ReportStats();
  641.     exit(1);
  642. }
  643. /*  FUNCTION
  644.         ReportStats - report program statistics.
  645.  
  646.     SYNOPSIS
  647.         void ReportStats();
  648.  
  649.     DESCRIPTION
  650.         ReportMem reports the maximum memory used, total number of file
  651.         nodes and total number of directory nodes for this invocation
  652.         of DiffDir, ONLY if the verbose option is turned on or if the
  653.         program terminates abnormally.
  654. */
  655. void
  656. ReportStats()
  657. {
  658.     printf("DiffDir: Files: %ld; directories: %ld; max memory: %ld bytes\n",
  659.            totalFiles, totalDirs, maxMemUsed);
  660. }
  661.  
  662.  
  663. /*  FUNCTION
  664.         stricmp - perform a case-insensitive string compare.
  665.  
  666.     SYNOPSIS
  667.         int stricmp(s1, s2)
  668.             char *s1, *s2;
  669.  
  670.     DESCRIPTION
  671.         Strings <s1> and <s2> are compared, ignoring differences in case.
  672.         A result code is returned according to the following:
  673.             0   => strings match
  674.            <0   => s1 < s2
  675.            >0   => s1 > s2
  676. */
  677.  
  678. int
  679. stricmp(s1, s2)
  680.     register char *s1, *s2;
  681. {
  682.     int c1, c2, cd;
  683.  
  684.     do {
  685.         c1 = tolower(*s1++);
  686.         c2 = tolower(*s2++);
  687.         if (cd = (c1 - c2)) break;
  688.     } while (c1 && c2);
  689.  
  690.     return cd;
  691. }
  692.  
  693. /*  FUNCTION
  694.         Usage - describe program usage and exit.
  695.  
  696.     SYNOPSIS
  697.         void Usage();
  698.  
  699.     DESCRIPTION
  700.         Usage is called when the user invokes DiffDir with incorrect
  701.         or insufficient parameters.  The correct invocation syntax
  702.         is displayed and the program is terminated.
  703. */
  704. Usage()
  705. {
  706.     puts("Usage: DiffDir [-c] [-s scriptfile] dirname1 dirname2");
  707.     MyExit();
  708. }
  709.  
  710. /*  FUNCTION
  711.         WriteFileInfo - write a full file description to standard output.
  712.  
  713.     SYNOPSIS
  714.         void WriteFileInfo(node)
  715.              FileNode *node;
  716.  
  717.     DESCRIPTION
  718.         WriteFileInfo writes complete info about the file specified by
  719.         <node> to the standard output.  This only happens when an error
  720.         occurs.
  721.  
  722. */
  723.  
  724. void
  725. WriteFileInfo(node)
  726.     FileNode *node;
  727.  
  728. {
  729.     static char flagSetNames[9] = {
  730.         '-', '-', '-', '-', 'a', 'p', 's', '?', '?'
  731.         };
  732.     static char flagClearNames[9] = {
  733.         'd', 'e', 'w', 'r', '-', '-', '-', '-', '-'
  734.         };
  735.  
  736.     ULONG   flags;
  737.     SHORT   i;
  738.     ULONG   mask;
  739.     char    temp[30];
  740.  
  741.     DSToStr(temp,"%02m-%02d-%02y %02h:%02n:%02s ",&node->date);
  742.     printf(temp);
  743.     flags = node->flags;
  744.     for (i = 0, mask = 1; i < 9; ++i, mask = (mask << 1) )
  745.         if (flags & mask)
  746.             temp[8 - i] = flagSetNames[i];
  747.         else
  748.             temp[8 - i] = flagClearNames[i];
  749.  
  750.     temp[9] = '\0';
  751.  
  752.     printf("%s %8ld %s\n", temp, node->size, node->name);
  753.     if (node->comment)
  754.         printf(": %s\n",node->comment);
  755. }
  756.  
  757.  
  758.